import { Plugin } from "vite";
import path from "path";
import {
  Project,
  Node,
  type JsxAttribute,
  JsxElement,
  ScriptKind,
} from "ts-morph";

interface TransCssModulePluginParams {
  //公共样式前缀
  globalClassSuffix?: string;
  //排除路径 正则匹配
  excludeFilePaths?: RegExp[];
}

function transFromTsx(
  params: TransCssModulePluginParams,
  fileText: string
): string {
  const project = new Project();

  const sourceFile = project.createSourceFile("", fileText, {
    scriptKind: ScriptKind.TSX,
  });

  const classList: Array<JsxElement> = [];

  function kebabToCamelCase(str: string): string {
    return new RegExp(`^${params.globalClassSuffix ?? "glb"}-`).test(str)
      ? str
      : str.replace(/-([a-z])/g, (match, letter) => letter.toUpperCase());
  }

  // 定义一个递归函数来遍历节点
  function traverseNodes(node: Node) {
    // 检查当前节点是否是 JsxElement
    if (Node.isJsxElement(node)) {
      // 不再递归遍历这个 JsxElement 的子节点
      classList.push(node);
      return;
    }

    // 对于其他类型的节点，继续递归遍历其子节点
    node.forEachChild(traverseNodes);
  }

  // 从源文件的根节点开始遍历
  sourceFile.forEachChild(traverseNodes);

  classList.forEach((attr) => {
    if (attr) {
      const parent = attr.getParent();
      if (Node.isParenthesizedExpression(parent)) {
        parent.setExpression(
          attr
            ?.getText()
            .replace(/class=["']([^"']+)["']/g, (match, className) => {
              const classNames = className.split(/\s+/).map(kebabToCamelCase);
              const camelCaseClassNames = classNames.map((val: string) =>
                new RegExp(`^${params.globalClassSuffix ?? "glb"}-`).test(
                  val
                ) || !val.trim()
                  ? val
                  : "${classes." + val + "}"
              );
              return `className={\`${camelCaseClassNames.join(" ")}\`}`;
            })
        );
      }
    }
  });

  sourceFile.addImportDeclaration({
    moduleSpecifier: "./index.module.css",
    defaultImport: "classes",
  });

  return sourceFile.getText();
}

export default function TransCssModulePlugin(
  params?: TransCssModulePluginParams
): Plugin {
  const opts = Object.assign(
    {
      globalClassSuffix: "glb",
      excludeFilePaths: [],
    },
    params ?? {}
  );

  return {
    name: "trans-css-module-plugin",
    transform(code, id) {
      const ids = id.split("/");
      if (
        id &&
        path.extname(ids[ids.length -1 ].split('?')[0]) === ".tsx" &&
        opts.excludeFilePaths.every((reg: RegExp) => !reg.test(id))
      ) {
        return transFromTsx(opts, code);
      }
      return null;
    },
  };
}
